package com.ujson.uaa.filter;

import com.ujson.base.common.config.GlobalConfig;
import com.ujson.base.common.dto.Res;
import com.ujson.base.common.enums.ErrorCode;
import com.ujson.base.common.utils.LogUtil;
import com.ujson.uaa.utils.JwtUtils;
import com.ujson.uaa.utils.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.util.matcher.RequestHeaderRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Objects;

/**
 * <h3>ujson-parent</h3>
 * <p>Jwt验证过滤器</p>
 *
 * @author JerryLiu  a1031757312@gmail.com
 * @version 1.0
 * @date 2021-11-20 13:12
 */
@Slf4j
public class TokenAuthenticationFilter extends BasicAuthenticationFilter {
    private GlobalConfig globalConfig;
    private JwtUtils jwtUtils;
    private RequestHeaderRequestMatcher requiresAuthenticationRequestMatcher;

    public TokenAuthenticationFilter(GlobalConfig globalConfig, JwtUtils jwtUtils, AuthenticationManager authenticationManager) {
        super(authenticationManager);
        this.globalConfig = globalConfig;
        this.jwtUtils = jwtUtils;
        this.requiresAuthenticationRequestMatcher = new RequestHeaderRequestMatcher(JwtUtils.authHeaderKey);
    }

    protected boolean requiresAuthentication(HttpServletRequest request,
                                             HttpServletResponse response) {
        return requiresAuthenticationRequestMatcher.matches(request);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        LogUtil.info(log, "token authentication filter");
        if (!this.requiresAuthentication(request, response)) {
            // 如果请求中没有token, 则放行, 让后续filter判断是否需要拦截
            chain.doFilter(request, response);
            return ;
        }

        UsernamePasswordAuthenticationToken token = null;
        try {
            token = getAuthentication(request);
        } catch (Exception ex) {
            LogUtil.error(log, "get authentication error.", ex);
        }
        if (Objects.isNull(token)) {
            ResponseUtil.out(response, Res.fail(ErrorCode.AUTH_FAIL));
            return ;
        } else {
            SecurityContextHolder.getContext().setAuthentication(token);
        }
        chain.doFilter(request, response);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        // 获取Token字符串，token 置于 header 里
        String token = request.getHeader(JwtUtils.authHeaderKey);
        if (StringUtils.isBlank(token)) {
            token = request.getParameter(JwtUtils.authHeaderKey);
        }
        if (token != null && !"".equals(token.trim())) {
            // 从Token中解密获取用户名
            String userName = jwtUtils.getUserNameFromToken(token);
            if (userName != null) {
                Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
                // 从Token中解密获取用户角色
                String role = jwtUtils.getUserRoleFromToken(token);
                // 角色不存在暂时先忽略
                if (StringUtils.isNotBlank(role)) {
                    // 将ROLE_XXX,ROLE_YYY格式的角色字符串转换为数组
                    String[] roles = role.split(",");
                    for (String s : roles) {
                        authorities.add(new SimpleGrantedAuthority(s));
                    }
                }
                return new UsernamePasswordAuthenticationToken(userName, token, authorities);
            }
            return null;
        }
        return null;
    }
}
